/*
    Videoplaza HTML5 DEMO integration
    @author: Nelson Diotto
    @date: 17 may 2012
    @last-updated: 12 Nov 2012
    @tested on
        desktop : Chrome 19.0, Safari 5.1.5, Firefox 12.0, IE 9 (Fail)
        mobile  : iPhone 4S + iOS 5.1, iPad 3 + iOS 5.1, iPad 1 + iOS 5.0,
                  Samsung Galaxy Tab 10.1
    
    ***************************************************************************************
    This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
    without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

    YOU ARE RESPONSIBLE FOR TESTING AND DEBUGGING YOUR IMPLEMENTATION. THIS CODE (player.js)
    WILL NOT BE MAINTAINED BY VIDEOPLAZA, THIS FILE ONLY PURPOSE IS TO BE USE AS AN EXAMPLE
    INTEGRATION BETWEEN VIDEOPLAZA SDK AND YOUR PLAYER. THIS CODE IS NOT FIT FOR DEPLOYMENT.
    ***************************************************************************************
*/

var vpConfigModule, videoplazaModule, consoleModule, initVideoplazaModule;

/******************************************************************************************
                                Load videoplazaModule sdk (404 and timeout)
******************************************************************************************/

/*
    As soon the page loads start loading videoplazaModule sdk, so it is ready waiting for the DOM
    to be ready.
*/
function loadScriptModule(myUrl, timeout, callback) {
    $.ajax({url: myUrl, dataType: "script", timeout: timeout})
            
        .done(function(data, textStatus, jqXHR) {
            callback($); // initVideoplazaModule()
            consoleModule.log('[VP SDK] ready');
        })
      
        .fail(function(XMLHttpRequest, textStatus, errorThrown) {
            consoleModule.error("Fail to load videoplazaModule plugin: " + errorThrown);
        });
}

/**********************************************************************************************
                                 Wait until the DOM is ready
**********************************************************************************************/

/*
    Wait until the DOM is ready to initiate all the code. The method initVideoplazaModule() will
    be called twice, once by loadScriptModule() and once by ready(). This allow for asynchronous
    loading the sdk and the DOM, but only starting when both are ready.
*/
$(document).ready(function() {
    //display the player that have been hidden on load.
    document.getElementById("module_my_video").style.display = 'inline';
    
    initVideoplazaModule($);
    consoleModule.log('[DOM] ready');
});

/**********************************************************************************************
                                initiate the player + videoplazaModule
**********************************************************************************************/


initVideoplazaModule = (function($) {
    "use strict";

    // Only proceed if both DOM and videoplazaModule sdk are ready
    if(!videoplazaModule || !$.isReady)
        return;

    /******************************************************************************************
                                    1. Config videoplazaModule sdk
    ******************************************************************************************/

    var vpHost, contentMetadata, requestSettings, myPlayerModule, adTrack, adRequest, adDisplay,
        adSection, myPlayerModuleSrc, myPlayerModulePoster, adResponse, playerState, ua, clickEvent;

    myPlayerModule    = document.getElementById("module_my_video");
    vpHost      = (vpConfigModule.vphost) ? vpConfigModule.vphost : "",
    ua          = navigator.userAgent,
    clickEvent  = (ua.match(/iPad/i)) ? "touchstart" : "click", //if ipad use touchstart as listener

    /*
        Metadata about your video content. It will be send to videoplazaModule back-end and in return
        get campaigns that match the 'category' and 'tags' you have added below.

        NAME                TYPE     REQUIRED                 DESCRIPTION
        category            String      No      The category you are targeting in Karbon UI
        tags                Array       No      Are descriptive keywords, e.g. "football", "beer"
        flags               Array       No      Are sdk extra config parameters, e.g. "nopostrolls"
        contentForm         String      No      Can be two types "shortForm" or "longForm"
                                                    1. shortForm = short clips, usually below 20min
                                                    2. longForm  = large videos, e.g. 2 hours film
        contentId           String      No      If you have an id for your video content
        contentPartner      String      No      If you have an partner for this content
        duration            Number      No      The length of your video file
    */
    contentMetadata = {
        category        : (vpConfigModule.category) ? vpConfigModule.category : "",
        tags            : (vpConfigModule.tags) ? vpConfigModule.tags : [],
        flags           : (vpConfigModule.flags) ? vpConfigModule.flags : [],
        contentForm     : "shortForm",
        contentId       : "",
        contentPartner  : "",
        duration        : (myPlayerModule.duration) ? myPlayerModule.duration : 0 //in seconds
    };

    /*
        Metadata about your player settings. It will help adjusting the advert to the correct
        size, bitrate, and display ads at correct position (pre, mid or post-roll).

        NAME                TYPE     REQUIRED                 DESCRIPTION
        height              Number      No      The height of your <video> player
        width               Number      No      The width of your <video> player
        playbackPosition    Array       No      Are the cuePoints for mid-rolls, if you have any.
        insertionPointType  String      Yes     The three types you need to know are:
                                                    1. onBeforeContent    = pre-rolls
                                                    2. playbackPosition   = mid-rolls
                                                    3. onContentEnd       = post-rolls
        maxBitRate          number      No      Please use the same bitRate as your video content or
                                                if you don't know or aren't sure just leave it UNDEFINED
                                                and videoplazaModule SDK will select the best bitRate for your
                                                device according to your userAgent.
    */
    requestSettings = {
        height              : myPlayerModule.height,
        width               : myPlayerModule.width,
        playbackPosition    : (vpConfigModule.playbackPosition) ? vpConfigModule.playbackPosition : [],
        insertionPointType  : "", //populated dynamic, see adRequest.newAdRequest();
        maxBitRate          : undefined
    };

    /******************************************************************************************
                                2. Make a new ad request to back-end
    ******************************************************************************************/

    adRequest = ({
        adCallModule : new videoplazaModule.core.AdCallModule(vpHost),

        /*
            The player need to make an ad request for every ad break (pre, mid and post-roll breaks).
            E.g. A player to displays pre and post-rolls should make 2 ad requests. The first request
            would have a insertionPointType = "onBeforeContent" and the second "onContentEnd".
        */
        newAdRequest : function(adtype) {
            requestSettings.insertionPointType = adtype;
            this.adCallModule.requestAds(contentMetadata, requestSettings, this.onSuccess, this.onFail);
        },

        onFail : function(errorMessage) {
            consoleModule.error(errorMessage);
            playerState.play();
        },

        onSuccess : function(ads) {
            consoleModule.log(ads);
            adResponse.filterAdsArr(ads);
        }
    });

    /******************************************************************************************
                                3. Check the ad response from back-end
    ******************************************************************************************/

    adResponse = ({
        adsArr : [],

        /*
            remove 'inventory' ads from the array, we only want to display 'available' ads.
        */
        filterAdsArr : function(ads) {

            ads.forEach(function(ad, index, array) {
                switch (ad.type)
                {
                    case 'standard_spot': // add to array queue of available ads to display
                        adResponse.adsArr.push(ad);
                        break;
                    case 'inventory': // do nothing, only track as  'available inventory'.
                        adDisplay.inventory(ad);
                        break;
                    default:
                        consoleModule.error('ad format ' + ad.type + ' not supported');
                        playerState.play();
                        break;
                }
            });
            this.checkNumOfAvailableAds();
        },

        /*
            An ad request returns an array of ad objects, you may have 0~N number of ads that need
            to be shown per ad break (pre, mid and post-roll breaks). E.g. "2 pre-rolls adverts"
        */
        checkNumOfAvailableAds : function() {

            // if pre-roll is empty: Play content.
            if (this.adsArr.length === 0 && requestSettings.insertionPointType === 'onBeforeContent') {
                playerState.play();
            }
            // if there is ads to display: start by showing the first one.
            else {
                adDisplay.standardSpot(this.adsArr[0]);
            }
        }
    });

    /******************************************************************************************
                                    4. Display ad creative(s)
    ******************************************************************************************/

    adDisplay = ({
        adState         : '',
        creative        : null,
        currentAd       : null,
        companionArr    : [],

        standardSpot : function(ad) {

            // 1. sort the Creative(s), it can be of two types: video ads or companion banners.
            for (var i = ad.creatives.length - 1; i >= 0; i--) {
                this.creative = ad.creatives[i];
                
                if (this.creative.id == 'video')
                    this.currentAd = this.creative;
                else if (this.creative.type =='companion')
                    this.companionArr.push(this.creative);
            }
            
            if (this.currentAd === null || this.currentAd.mediaFiles[0] === undefined) {
                consoleModule.log('[STANDARD SPOT] bad ad format or undefined media file');
                adSection.videoCompleted(requestSettings.insertionPointType);
                return;
            }

            // 2. play the ad creative
            playerState.play({'src':this.currentAd.mediaFiles[0].uri, 'controls':false, 'poster':false});

            // 3. add click-trough to the ad.
            myPlayerModule.addEventListener(clickEvent, adDisplay.standardSpotClickThrough);

            // 4. display companion banner(s), if available.
            this.companionBanner(this.companionArr);

            // 5. track the video ad.
            adTrack.AD_IMPRESSION(ad);
            adTrack.AD_START();
            adTrack.AD_QUARTILES(ad.creatives[0].duration);
        },

        standardSpotClickThrough : function() {
            myPlayerModule.removeEventListener(clickEvent, adDisplay.standardSpotClickThrough);
            
            window.open(adDisplay.currentAd.clickThroughUri);
            adTrack.AD_CLICK_THROUGH(); //track

            // on user click-through, pause the player.
            myPlayerModule.controls = true;
            myPlayerModule.pause();

            // after click-through, remove click listener.
            myPlayerModule.addEventListener(clickEvent, function onClickTogglePause() {
                this.removeEventListener(clickEvent, onClickTogglePause);
                myPlayerModule.play();
            }, false);
        },

        inventory : function(ad) {
            adTrack.AD_IMPRESSION(ad); // track
        },

        /*
            An ad object may have 0~N number of companion banners. E.g. When a pre-roll is
            displayed two companion MPU banners could appear on both sides of the player.
        */
        companionBanner : function(companionArr) {
            if (companionArr.length === 0)
                return;

            for (var i = companionArr.length - 1; i >= 0; i--) {

                var iframe = '<iframe scrolling= no frameborder= 0' +
                                ' width='   + companionArr[i].width +
                                ' height='  + companionArr[i].height +
                                ' src='     + companionArr[i].resource +
                             '</iframe>';

                var cb = document.getElementById(companionArr[i].zoneId);

                if(cb)
                    cb.innerHTML = iframe;

                adTrack.COMPANION_IMPRESSION(); // track every companion banner
            }
        }
    });

    /******************************************************************************************
                                        5. Ad tracking
    ******************************************************************************************/

    /*
        Tracking those events are mandatory: impression, creativeView, start, complete and
        clickThrough. Tracking quartiles is highly recommended.

        Other tracking events are optional and should only be implemented according to client
        requirement.
    */
    adTrack = ({
        tracker         : new videoplazaModule.core.Tracker(),
        firstQuartile   : 0,
        midpoint        : 0,
        thirdQuartile   : 0,

        COMPANION_IMPRESSION    : function() {
            this.tracker.track(adDisplay.currentAd, videoplazaModule.core.Tracker.trackingEvents.creative.creativeView);
            consoleModule.log('[TRACK] creativeView');
        },
        AD_IMPRESSION           : function(ad) {
            this.tracker.track(ad, videoplazaModule.core.Tracker.trackingEvents.ad.impression);
            consoleModule.log('[TRACK] impression');
        },
        AD_START                : function() {
            this.tracker.track(adDisplay.currentAd, videoplazaModule.core.Tracker.trackingEvents.creative.start);
            consoleModule.log('[TRACK] start');
        },
        AD_COMPLETE             : function() {
            this.tracker.track(adDisplay.currentAd, videoplazaModule.core.Tracker.trackingEvents.creative.complete);
            consoleModule.log('[TRACK] complete');
        },
        AD_CLICK_THROUGH        : function() {
            this.tracker.track(adDisplay.currentAd, videoplazaModule.core.Tracker.trackingEvents.creative.clickThrough);
            consoleModule.log('[TRACK] clickThrough');
        },
        AD_QUARTILES            : function(duration) {

            this.firstQuartile   = Math.round((duration / 100) * 25);
            this.midpoint        = Math.round((duration / 100) * 50);
            this.thirdQuartile   = Math.round((duration / 100) * 75);

            myPlayerModule.addEventListener("timeupdate", adTrack.trackQuartiles);
        },
        trackQuartiles          : function() {
                
            switch (Math.round(this.currentTime))
            {
                case adTrack.firstQuartile:
                    adTrack.tracker.track(adDisplay.currentAd, videoplazaModule.core.Tracker.trackingEvents.creative.firstQuartile);
                    adTrack.firstQuartile = NaN; //stop tracking it twice
                    consoleModule.log('[TRACK] firstQuartile');
                    return;
                case adTrack.midpoint:
                    adTrack.tracker.track(adDisplay.currentAd, videoplazaModule.core.Tracker.trackingEvents.creative.midpoint);
                    adTrack.midpoint = NaN;
                    consoleModule.log('[TRACK] midpoint');
                    return;
                case adTrack.thirdQuartile:
                    adTrack.tracker.track(adDisplay.currentAd, videoplazaModule.core.Tracker.trackingEvents.creative.thirdQuartile);
                    myPlayerModule.removeEventListener("timeupdate", adTrack.trackQuartiles);
                    adTrack.thirdQuartile = NaN;
                    consoleModule.log('[TRACK] thirdQuartile');
                    return;
            }
        }
    });

    /******************************************************************************************
                                        6. Ad section
    ******************************************************************************************/

    /*
        A complete inline/standard ad section is composed by pre, mid and post-rolls. Anyhow
        are rare cases were clients use all those formats in a single ad section. Your player
        does not need to support all those formats! It only need to support formats required
        by your client.
    */

    adSection = ({

        /*
            When the user click play you need to interrupt the player and make an ad request
            passing the correct metadata (contentMetadata and requestSettings) to videoplazaModule
            back-end.

            You also need to add event listeners to perform different tasks on video start and
            completed. E.g. When the pre-roll is completed you should have a listener listening
            for video "ended", so it can trigger the start of the next video.
        */
        videoStart : function(type) {
            consoleModule.log('[NEW AD REQUEST] ad type: ' + type);

            myPlayerModule.pause();
            adRequest.newAdRequest('onBeforeContent');
        },

        /*
            When one ad is complete you should check if there is no more ads to be shown. If an ad
            is being show you should hide the player controls, when all ads have been displayed the
            player should display it's controls again.
        */
        videoCompleted : function(type) {
            consoleModule.log('[AD COMPLETE PLAYING] ad type: ' + type);

            // after ad have played remove it from ad "queue" array
            adResponse.adsArr.shift();

            // if no ads left to show, start content
            if (adResponse.adsArr.length === 0) {
                playerState.play();
            }
            else {
                // display next ad in the queue
                adDisplay.standardSpot(adResponse.adsArr[0]);
            }
        }
    });

    /******************************************************************************************
                                Player different states throughout playback
    ******************************************************************************************/

    playerState = ({

        /*
            As soon the player loads the video file, store the src for later use, it will
            be added back into the player after all ads have been displayed
        */
        init : function() {
            myPlayerModuleSrc     = myPlayerModule.currentSrc;
            myPlayerModulePoster  = myPlayerModule.poster;
        }(),

        play : function(config) {

            if (!config) {
                this.reset();
            }
            else {
                myPlayerModule.src        = (config.src) ? config.src : myPlayerModuleSrc;
                myPlayerModule.poster     = (config.poster === false) ? '' : myPlayerModulePoster;
                myPlayerModule.controls   = (config.controls !== undefined) ? config.controls : true;

                /*
                    Instead of listening to onEnded event use timeupdate to check to the
                    video completion. Fix for Safari Browser not firing onEnded.
                */
                myPlayerModule.addEventListener("timeupdate", function onTimeUpdate() {

                    if (this.currentTime === this.duration){
                        this.removeEventListener("timeupdate", onTimeUpdate);

                        consoleModule.log('[PLAYER] ended');
                        adSection.videoCompleted('onBeforeContent');

                        adTrack.AD_COMPLETE(); //track
                    }
                },false);
            }

            // before start playing, give enough time to the player to change the video src
            myPlayerModule.addEventListener('loadstart', function playerPlay() {
                this.removeEventListener('loadstart', playerPlay);
                consoleModule.log('[PLAYER] play');
                myPlayerModule.play();
            }, false);
        },

        /*
            Reset all player changes to original player state.
        */
        reset : function() {
            myPlayerModule.poster     = myPlayerModulePoster;
            myPlayerModule.src        = myPlayerModuleSrc;
            myPlayerModule.controls   = true;

            myPlayerModule.removeEventListener(clickEvent, adDisplay.standardSpotClickThrough);
        }
    });

    // When the user clicks play, make an ad request for a new pre-roll
    myPlayerModule.addEventListener("play", function videoStart() {
        this.removeEventListener('play', videoStart);
        adSection.videoStart('onBeforeContent');
    }, false);

}); // end initVideoplazaModule


/******************************************************************************************
                                            Helper(s)
******************************************************************************************/

if (!Array.prototype.forEach) {
    Array.prototype.forEach = function(fun /*, thisp*/) {
        if (typeof fun != "function")
            throw new TypeError();

        for (var i = this.length - 1; i >= 0; i--) {
            if (i in this)
                fun.call(arguments[1], this[i], i, this);
        }
    };
}